package com.rockcent.mall.lib.repository;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.rockcent.common.common.PageResult;
import com.rockcent.common.common.SQLListDoing;
import com.rockcent.common.common.SQLObjectDoing;
import com.rockcent.common.common.SQLPageDoing;
import com.rockcent.common.domain.enumclass.Whether;
import com.rockcent.mall.lib.domain.MerchantCoupon;
import com.rockcent.mall.lib.domain.Product;
import com.rockcent.mall.lib.domain.enumclass.CouponType;
import com.rockcent.mall.lib.domain.enumclass.CouponUseScope;
import com.rockcent.mall.lib.domain.enumclass.MerchantCouponStatus;
import com.rockcent.mall.lib.dto.IntegrationCouponRespDto;
import com.rockcent.mall.lib.dto.MerchantCouponDto;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

/**
 * Created by zhangjh on 2016/10/11.
 */
@Repository
public class MerchantCouponRepositoryImpl implements MerchantCouponRepositoryCustom {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;

    private Logger log = LoggerFactory.getLogger(MerchantCouponRepositoryImpl.class);


    @Override
    public PageResult findByMerchantIdAndSharing(Long mallId, Long merchantId, String isSharing, String name, String couponType, String isOnLine, String orderFieldName, String isDesc, String status, PageResult pageResult) {
        SQLPageDoing doing = (sql, fields, params, typeReferenceMap) -> {
            sql.append("SELECT %s FROM coupon c  LEFT JOIN `merchant` m ON m.id=c.merchant_id");
            sql.append(" WHERE c.`is_delete` = 'NO'");
            sql.append(" AND c.`leave_total` >0");
            sql.append(" AND c.integration = 'NO'");
//            sql.append(" AND c.`id` not in(SELECT c.id FROM coupon c where c.status in('SOLDOUT','EXPIRED'))");
            if (StringUtils.isNotBlank(name)) {
                sql.append(" And c.name like :name OR c.bar_code like :barCode");
                params.put("name", "%" + name + "%");
                params.put("barCode", "%" + name + "%");
            }
            if (StringUtils.isNotBlank(status)) {
                sql.append(" AND c.`status`=:status");
                params.put("status", status);
            } else {
                sql.append(" AND c.`status`='ISSUED'");
            }

            if (StringUtils.isNotBlank(isSharing)) {

                if (Whether.NO.toString().equals(isSharing)) {
                    sql.append(" And c.merchant_id=:merchantId");
                    params.put("merchantId", merchantId);
                }
                if (Whether.YES.toString().equals(isSharing)) {
                    sql.append(" And c.sharing=:isSharing");
                    sql.append(" And c.merchant_id<>:merchantId");
                    params.put("isSharing", isSharing);
                    params.put("merchantId", merchantId);
                }
            }

            if (StringUtils.isNotBlank(isOnLine)) {
                if (Whether.YES.toString().equals(isOnLine)) {
                    sql.append(" And c.use_scope not in('" + CouponUseScope.ALL_STORE.toString() + "','" + CouponUseScope.PART_STORE.toString() + "')");
                } else if (Whether.NO.toString().equals(isOnLine)) {
                    sql.append(" And c.use_scope in('" + CouponUseScope.ALL_STORE.toString() + "','" + CouponUseScope.PART_STORE.toString() + "')");
                }


            }
            if (null != mallId) {
                sql.append(" And c.mall_id=:mallId");
                params.put("mallId", mallId);
            }
            if (StringUtils.isNotBlank(couponType)) {
                sql.append(" And c.type=:couponType");
                params.put("couponType", couponType);
            }
            //按照字段排序
            if (StringUtils.isNotBlank(orderFieldName)) {
                if (StringUtils.isNotBlank(isDesc)) {
                    if (Whether.valueOf(isDesc).equals(Whether.YES)) {
                        sql.append(" ORDER BY c." + orderFieldName + " DESC");
                    }
                    if (Whether.valueOf(isDesc).equals(Whether.NO)) {
                        sql.append(" ORDER BY  c." + orderFieldName + " ASC");
                    }

                } else {
                    sql.append(" ORDER BY c." + orderFieldName + " DESC");
                }
            } else {
                sql.append(" ORDER BY c.date_disabled  DESC");
            }
            if (StringUtils.isNotBlank(couponType)) {
                if (CouponType.DISCOUNT.toString().equals(couponType)) {
                    fields.append("c.id,c.integration,c.merchant_id,c.name,m.name as merchantName,c.use_scope,c.limit_amount, c.date_disabled,c.type,c.discount,c.sharing,c.logo_url,c.leave_total");
                }
                if (CouponType.VOUCHERS.toString().equals(couponType)) {
                    fields.append("c.id,c.integration,c.merchant_id,c.name,m.name as merchantName,c.use_scope,c.limit_amount, c.date_disabled,c.type,c.face_value,c.sharing,c.logo_url,c.leave_total");
                }
            } else {
                fields.append("c.id,c.integration,c.merchant_id,c.name,m.name as merchantName,c.use_scope,c.limit_amount, c.date_disabled,c.type,c.discount,c.face_value,c.sharing,c.logo_url,c.leave_total");
            }
        };
        return doing.go(pageResult, MerchantCouponDto.class, objectMapper, jdbcTemplate, log);
    }

    @Override
    public PageResult findByMerchantId(Long merchantId, String name, String couponType, String status, PageResult pageResult) {
        SQLPageDoing doing = (sql, fields, params, typeReferenceMap) -> {
            if (StringUtils.isNotBlank(status) && (MerchantCouponStatus.valueOf(status) == MerchantCouponStatus.WAIT_AUDIT || MerchantCouponStatus.valueOf(status) == MerchantCouponStatus.UNAUDITED)) {
                sql.append("SELECT %s FROM coupon c left join integration_coupon_audit ca on c.id = ca.coupon_id and c.integration ='YES' WHERE c.`is_delete`='NO'");
                fields.append("c.id,c.integration, c.status, c.quantity,c.merchant_id,c.name,c.face_value,c.limit_count,c.limit_amount,c.date_issued,c.date_disabled,c.use_scope,c.type,c.discount,c.sharing,c.leave_total,c.logo_url,c.date_created, ca.remark");
            } else {
                sql.append("SELECT %s FROM coupon c WHERE c.`is_delete`='NO'");
                fields.append("c.id,c.integration, c.quantity,c.merchant_id,c.name,c.face_value,c.limit_count,c.limit_amount,c.date_issued,c.date_disabled,c.use_scope,c.type,c.discount,c.sharing,c.leave_total,c.logo_url,c.date_created");
            }
            sql.append(" And c.merchant_id=:merchantId");

            params.put("merchantId", merchantId);

            if (StringUtils.isNotBlank(name)) {
                sql.append(" And (c.name like :name OR c.id like :id)");
                params.put("name", "%" + name + "%");
                params.put("id", "%" + name + "%");
            }
            if (StringUtils.isNotBlank(status)) {
                if (MerchantCouponStatus.valueOf(status) == MerchantCouponStatus.ISSUED) {
                    sql.append(" AND c.status in (:status)");
                    params.put("status", Arrays.asList(MerchantCouponStatus.ISSUED.toString(), MerchantCouponStatus.WAIT_ISSUE.toString()));
                } else if (MerchantCouponStatus.valueOf(status) == MerchantCouponStatus.WAIT_AUDIT) {
                    sql.append(" AND c.status in (:status)");
                    params.put("status", Arrays.asList(MerchantCouponStatus.UNAUDITED.toString(), MerchantCouponStatus.WAIT_AUDIT.toString()));
                } else {
                    sql.append(" AND c.status=:status");
                    params.put("status", status);
                }
            }

            if (StringUtils.isNotBlank(couponType) && CouponType.ALL != CouponType.valueOf(couponType)) {
                sql.append(" And c.type=:couponType");
                params.put("couponType", couponType);
            }
            if (StringUtils.isNotBlank(status)) {
                if (MerchantCouponStatus.valueOf(status) == MerchantCouponStatus.ISSUED || MerchantCouponStatus.valueOf(status) == MerchantCouponStatus.WAIT_AUDIT) {
                    sql.append(" ORDER BY  c.date_created desc");
                }
                //已下架 按下架时间排序
                if (MerchantCouponStatus.valueOf(status) == MerchantCouponStatus.SOLDOUT) {
                    sql.append(" ORDER BY  c.last_updated desc");
                }
                //已过期，按过期时间排序
                if (MerchantCouponStatus.valueOf(status) == MerchantCouponStatus.EXPIRED) {
                    sql.append(" ORDER BY  c.date_disabled desc");
                }
            } else {
                sql.append(" ORDER BY  c.date_created desc");
            }
        };
        return doing.go(pageResult, MerchantCouponDto.class, objectMapper, jdbcTemplate, log);
    }

    @Override
    public Long countCouponVerified(Long merchantId, Long mallId, Long couponId, PageResult pageResult) {


        SQLObjectDoing doing = (sql, fields, params, typeReferenceMap) -> {
            sql.append(" SELECT %s FROM customer_coupon_wallet ce LEFT JOIN coupon c ON c.`id`=ce.`coupon_id`");
            sql.append(" WHERE ce.merchant_id=:merchantId");
            sql.append(" And ce.mall_id=:mallId");
            sql.append(" And ce.is_delete='NO'");
            sql.append(" And ce.coupon_id=:couponId");
            sql.append(" And ce.verification_way in('SCAN','KEY_IN','SELF_CONFIRM')");
            params.put("merchantId", merchantId);
            params.put("mallId", mallId);
            params.put("couponId", couponId);
            fields.append("c.id,sum(ce.use_num) as verifiedCount");
        };
        MerchantCouponDto dto = (MerchantCouponDto) doing.go(MerchantCouponDto.class, jdbcTemplate, log, objectMapper);

        return dto.getVerifiedCount();
    }

    @Override
    public Long countCouponSendCustomer(Long merchantId, Long mallId, Long couponId, PageResult pageResult) {

        SQLObjectDoing doing = (sql, fields, params, typeReferenceMap) -> {
            sql.append("SELECT %s FROM customer_coupon_wallet cw LEFT JOIN coupon c ON c.id=cw.coupon_id");
            sql.append(" WHERE cw.coupon_id=:couponId");
            sql.append(" AND cw.mall_id=:mallId");
            sql.append(" AND cw.merchant_id=:merchantId");
            params.put("merchantId", merchantId);
            params.put("mallId", mallId);
            params.put("couponId", couponId);
            fields.append("cw.coupon_id,count(cw.coupon_id) as sendTotal");
        };
        MerchantCouponDto dto = (MerchantCouponDto) doing.go(MerchantCouponDto.class, jdbcTemplate, log, objectMapper);


        return dto.getSendTotal();
    }

    @Override
    public List<MerchantCoupon> findByMerchantIdAndCouponIds(Long merchantId, List<Long> couponIds) {
        SQLListDoing doing = (sql, fields, params, map) -> {
            sql.append("select %s from coupon c where c.merchant_id=:merchantId and c.show_on_merchant =:showOnMerchant and c.status=:status ");
            if (couponIds != null && couponIds.size() > 0) {
                sql.append("and c.id in(:couponIds)");
                params.put("couponIds", couponIds);
            }
            sql.append(" order by c.date_created desc ");
            params.put("merchantId", merchantId);
            params.put("showOnMerchant", Whether.YES.toString());
            params.put("status", MerchantCouponStatus.ISSUED.toString());
            fields.append("c.*");

        };
        return doing.go(MerchantCoupon.class, objectMapper, jdbcTemplate, log);
    }

    @Override
    public List<MerchantCoupon> getUseableCoupons(Product product) {
        SQLListDoing doing = (sql, fields, params, map) -> {
            sql.append("select %s from coupon c left join coupon_merchant_products cp on c.id = cp.coupon_id ");
            sql.append(" where c.merchant_id = :merchantId and c.show_on_merchant= :showOnMerchant and c.status= :status and c.use_scope= :useScope");
            sql.append(" and cp.product_id = :productId");
            fields.append("c.*");
            params.put("merchantId", product.getMerchantId());
            params.put("showOnMerchant", Whether.YES.toString());
            params.put("status", MerchantCouponStatus.ISSUED.toString());
            params.put("useScope", CouponUseScope.PART_PRODUCT.toString());
            params.put("productId", product.getId());

        };
        return doing.go(MerchantCoupon.class, objectMapper, jdbcTemplate, log);

    }

    @Override
    public Long getCouponCount(Long mallId, Long merchantId) {
        SQLObjectDoing<Long> doing = (sql, fields, params, typeReferenceMap) -> {
            sql.append("select %s from coupon c");
            sql.append(" WHERE c.merchant_id=:merchantId");
            sql.append(" And c.mall_id=:mallId");
            sql.append(" and c.is_delete='NO'");
            sql.append(" and c.status='ISSUED'");

            params.put("merchantId", merchantId);
            params.put("mallId", mallId);

            fields.append("COUNT(1)");
        };
        return doing.go(Long.class, jdbcTemplate, log, objectMapper);

    }


    @Override
    public PageResult<MerchantCoupon> findByShowOnCenterAndStatusAndMallId(Whether ShowOnCenter, String type, MerchantCouponStatus status, Long mallId, PageResult pageResult) {
        SQLPageDoing doing = (sql, fields, params, map) -> {
            sql.append("select %s from coupon c where c.show_on_center = :showOnCenter and c.status = :status and c.mall_id = :mallId ");
            if (type != null && !"NEW".equals(type)) {
                sql.append(" and c.type = :type ");
                params.put("type", type);
            }
            sql.append(" order by c.date_created desc ");
            params.put("showOnCenter", ShowOnCenter.toString());
            params.put("status", status.toString());
            params.put("mallId", mallId);
            fields.append("c.*");
        };
        return doing.go(pageResult, MerchantCoupon.class, objectMapper, jdbcTemplate, log);
    }


    @Override
    public List<Long> findAllExpiredCouponIds() {
        SQLListDoing<Long> doing = (sql, fields, params, typeReferenceMap) -> {
            sql.append("select %s from coupon");
            sql.append(" where date_disabled<now() and status in( 'WAIT_ISSUE','ISSUED') ");
            fields.append("distinct id");
        };

        return doing.go(Long.class, jdbcTemplate, log, objectMapper);
    }

    @Override
    public PageResult<MerchantCouponDto> findIntegrationCouponPage(Long mallId, String name, String merchantName, BigDecimal faceValue, String status, String integrationStatus, String type, PageResult pageResult) {
        SQLPageDoing<MerchantCouponDto> doing = (sql, fields, params, map) -> {
            sql.append("select %s from integration_coupon_audit ca  left join  coupon c on c.id = ca.coupon_id left join  merchant m on c.merchant_id = m.id left join integration_channel n on c.integration_channel_id=n.id where c.integration = 'YES'  and c.mall_id = :mallId");
            if (CouponType.ALL != CouponType.valueOf(type)) {
                sql.append(" and c.type = :type ");
                params.put("type", type);
            }
            if (status.equals("WAIT_AUDIT")) {
                sql.append(" and ca.status = 'WAIT_AUDIT'");
//                params.put("status", status);
            } else if (status.equals("WAIT_ISSUE")) {
                log.info("======================>----》integrationStatus:" + integrationStatus);
                if (integrationStatus.equals("WAIT_ISSUE")) {
                    sql.append(" and ca.status in ('PASS_AUDIT','FAIL_AUDIT','SOLDOUT') ");
                } else if (!integrationStatus.equals("SOLDOUT")) {
                    sql.append(" and ca.status = :status");
                    params.put("status", integrationStatus);
                    sql.append(" and c.status <>'SOLDOUT' ");
                } else if (integrationStatus.equals("SOLDOUT")) {
                    sql.append(" and c.status = 'SOLDOUT' ");
                }
            }

            if (faceValue != null) {
                sql.append(" and c.face_value = :faceValue ");
                params.put("faceValue", faceValue);
            }

            if (StringUtils.isNotBlank(name)) {
                sql.append(" And c.name like :name");
                params.put("name", "%" + name + "%");
            }

            if (StringUtils.isNotBlank(merchantName)) {
                sql.append(" And m.name like :merchantName");
                params.put("merchantName", "%" + merchantName + "%");
            }

            sql.append(" order by c.date_created desc ");
//            params.put("status", status);
            params.put("mallId", mallId);
            fields.append("c.*, n.name as channelName,m.name merchantName, ca.last_updated auditTime,case when c.status='SOLDOUT' THEN 1 ELSE 0 END AS can_stop,  case when c.integration_channel_id  is not null or c.status='SOLDOUT' THEN 1 ELSE 0 END AS can_put ");
        };
        return doing.go(pageResult, MerchantCouponDto.class, objectMapper, jdbcTemplate, log);
    }

    /**
     * @param integrationChannelId
     * @return
     */
    @Override
    public Long queryTotalNum(Long integrationChannelId) {
        SQLObjectDoing<Long> result = (sql, fields, params, typeReferenceMap) -> {
            sql.append("select %s from coupon where integration = 'YES' and integration_channel_id= :integrationChannelId");
            params.put("integrationChannelId", integrationChannelId);
            fields.append(" sum(quantity) ");
        };
        return result.go(Long.class, jdbcTemplate, log, objectMapper);
    }

    /**
     * @param mallId
     * @param integrationChannelId
     * @return
     */
    @Override
    public Long queryMaxBatchPutNo(Long mallId, Long integrationChannelId) {
        SQLObjectDoing<Long> result = (sql, fields, params, typeReferenceMap) -> {
            sql.append("select %s from coupon where integration_channel_id= :integrationChannelId and mall_id =:mallId and integration='YES' for update");
            params.put("integrationChannelId", integrationChannelId);
            params.put("mallId", mallId);
            fields.append(" case when max(batch_put_no) is null then 0 else max(batch_put_no) end");
        };
        return result.go(Long.class, jdbcTemplate, log, objectMapper);
    }

    /**
     * @param mallId
     * @param channelId
     * @return
     */
    @Override
    public BigDecimal queryAvailableAmountByMallId(Long mallId, Long channelId) {

        return null;
    }


    /**
     * @param yes
     * @param integrationChannelId
     * @param status
     * @return
     */
    @Override
    public List<IntegrationCouponRespDto> findByIntegrationAndIntegrationChannelIdAndStatus(Whether yes, Long integrationChannelId, MerchantCouponStatus status) {

        SQLListDoing<IntegrationCouponRespDto> doing = (sql, fields, params, typeReferenceMap) -> {
            sql.append("select %s from coupon where integration = 'YES' and status='ISSUED' and integration_channel_id= :integrationChannelId");
            params.put("integrationChannelId", integrationChannelId);
            fields.append(" id,face_value,limit_amount,date_enabled,date_disabled");
        };

        return doing.go(IntegrationCouponRespDto.class, jdbcTemplate, log, objectMapper);

    }


    /**
     * @param yes
     * @param integrationChannelId
     * @param issued
     * @param faceVal
     * @return
     */
    @Override
    public IntegrationCouponRespDto findOneByIntegrationAndIntegrationChannelIdAndStatusAndFaceVal(Whether yes, Long integrationChannelId, MerchantCouponStatus issued, BigDecimal faceVal) {
        SQLObjectDoing<IntegrationCouponRespDto> result = (sql, fields, params, typeReferenceMap) -> {

            sql.append("select %s from coupon where is_delete='NO' and integration = 'YES' and status='ISSUED' and integration_channel_id= :integrationChannelId");
            sql.append(" and face_value=:faceVal");
            sql.append(" limit 1");
            params.put("integrationChannelId", integrationChannelId);
            params.put("faceVal", faceVal);
            fields.append(" id,face_value,limit_amount,date_enabled,date_disabled ");


        };
        return result.go(IntegrationCouponRespDto.class, jdbcTemplate, log, objectMapper);
    }
}
